package fr.zombieGame;

/**
 * Polytech' Nice - Sophia Antipolis / SI3 / POO2
 * Annee 2011_2012
 * 
 * OMG Zombies !
 * 
 * Classe Humain : modelisation d'un humain
 *                 Humain herite de Personnage
 * 
 * @author J. Harnetiaux et J. Lionne
 */

import java.util.List;
import java.util.Random;
import fr.comportement.*;
import com.simulator.*;

public class Humain extends Personnage{

    private static final int AGE_REPRODUCTION = 15;
    private static final int AGE_MAX = 75;
    private static final double REPRODUCTION_PROBABILITE = 0.5;
    private static final int NOMBRE_ENFANTS_MAX = 3;
    private static final double TRANSFORMATION_ZOMBIE_PROBABILITE = 0.01;
    private static final Random rand = Randomizer.getRandom();

    private int age;
    private boolean mordu;
    private boolean futurZombie;
    private int toursSansManger;

    /**
     * Premier constructeur normal
     * Applique le comportement par defaut de la classe Personnage
     * @param nom
     *          Nom de l'humain
     * @param pointsDeVie
     *          Nombre de points de vie initial de l'humain
     */
    public Humain(String nom, int pointsDeVie, Field field, Location location, boolean ageRandom) {
        super(nom, pointsDeVie, field, location);
        force = 1;
        age = 0;
        mordu = false;
        futurZombie = false;
        toursSansManger = 0;
        if(ageRandom) age = rand.nextInt(AGE_MAX);
    }

    /**
     * Second constructeur normal
     * @param nom
     *          Nom de l'humain
     * @param pointsDeVie
     *          Nombre de points de vie initial de l'humain
     * @param deplacement
     *          Strategie de deplacement
     * @param espritCombatif
     *          Strategie de combat
     * @param soutient
     */
    public Humain(String nom, int pointsDeVie, Field field, Location location,
            Deplacement deplacement, EspritCombatif espritCombatif, Soutien soutien, boolean ageRandom) {
        super(nom, pointsDeVie, field, location, deplacement, espritCombatif, soutien);
        force = 1;
        age = 0;
        mordu = false;
        toursSansManger = 0;
        if(ageRandom) age = rand.nextInt(AGE_MAX);
    }
    
    /**
     * Accesseur age
     * @return age : age de l'humain
     */
    public int getAge(){
    	return age;
    }
    
    /**
     * Mutateur de l'age
     * @param age
     */
    public void setAge(int age){
    	this.age = age;
    }

    /**
     * @return true si l'humain a ete mordu. False sinon
     */
    public boolean isMordu() {
        return mordu;
    }

    /**
     * @return true si l'humain sera transforme en zombie a sa mort, false sinon
     */
    public boolean isFuturZombie() {
        return futurZombie;
    }

    /**
     * Pour nourrir un humain
     */
    public void setNourri(){
        toursSansManger = 0;
    }
    /**
     *
     * @return true si l'humain est nourri, false sinon
     */
    public boolean getNourri(){
    	if(toursSansManger == 0)
    		return true;
    	else
    		return false;
    }
    
    /**
     * Accesseur toursSansManger
     * @return toursSansManger
     */
    public int getToursSansManger(){
    	return toursSansManger;
    }
    
    /**
     * Fixer le nombre de tour sans manger
     * @param nombreTour
     */
    public void setToursSansManger(int nombreTour){
    	toursSansManger = nombreTour;
    }


    /**
     * Indique si l'humain va etre transforme en zombie ou s'il va mourir
     */
    @Override
    public void setMort() {
        if(rand.nextDouble() <= TRANSFORMATION_ZOMBIE_PROBABILITE) futurZombie = true;
        else super.setMort();
    }

    /**
     * @param mordu
     *          Indicateur de morsure
     */
    public void setMordu(boolean mordu) {
        this.mordu = mordu;
    }

    /**
     * Change la strategie de deplacement de l'humain
     * Un humain ne peut que marcher ou courrir
     */
    @Override
    public void setDeplacement(Deplacement deplacement) {
        // Ne rien faire si la strategie de deplacement proposee n'est pas valide pour un humain
        //
        if(!(deplacement instanceof Marcher) || !(deplacement instanceof Courir)) return;

        // Appliquer la strategie de deplacement
        //
        this.deplacement = deplacement;
    }

    /**
     * Change la strategie de combat de l'humain
     * Un humain ne peut pas se battre a mains nues ou mordre un ennemie
     */
    @Override
    public void setEspritCombatif(EspritCombatif espritCombatif) {
        // Ne rien faire si la strategie de combat proposee n'est pas valide pour un humain
        //
        if(espritCombatif instanceof Morsure || espritCombatif instanceof CombatMainsNues) return;

        // Appliquer la strategie de combat
        //
        this.espritCombatif = espritCombatif;
    }

    /**
     * Change la strategie de soutient de l'humain
     */
    @Override
    public void setSoutien(Soutien soutien) {
        // Appliquer la strategie de soutient
        //
        this.soutien = soutien;
    }

    /**
     * Transforme l'humain en vampire
     * Un nouveau vampire est toujours assoiffe de sang
     * @return le vampire correspondant a l'humain transforme (meme nom et meme nombre de points de vie)
     */
    public Vampire transformerEnVampire() {
        Vampire vampire = new Vampire(nom, pointsDeVie, field, location);
        vampire.setAssoiffe(true);
        return vampire;
    }

    /**
     * Transforme l'humain en zombie
     * @return le zombie correspondant a l'humain transforme (meme nom et meme nombre de points de vie)
     */
    public Zombie transformerEnZombie() {
        Zombie zombie = new Zombie(nom, pointsDeVie, field, location);
        return zombie;
    }

    /**
     * Joue le comportement de l'humain
     * @param humains
     *          Liste qui va contenir les eventuels enfants de l'humain
     */
    public void jouer(List<Humain> humains) {
        // Incrementer l'age de l'humain
        //
        incrementAge();

        // Si l'humain est vivant :
        //    - Donner naissance a de nouvaux humains
        //    - Se deplacer (s'il ne peut pas bouger il mourra de surpopulation)
        //    - Verifier si un personnage se trouve a cote
        //
        if (estVivant) {
            donnerNaissance(humains);
            if(espritCombatif instanceof Pacifiste) {
                deplacement.bouger(this);

                // Verifier si le personnage n'est pas mort de surpopulation
                //
                if (estVivant) trouverPersonnage(location);
                else setMort();
            }
            else {
                // Se deplacer a une localisation ou se trouve un ennemi
                //
                Location localisation = trouverEnnemi(location);
                if (localisation == null) {
                    // Pas d'ennemi aux alentours - se deplacer sur une localisation dispo
                    //
                    localisation = field.freeAvailableLocation(location, deplacement.getVitesse());
                }
                // Verifier s'il est possible de bouger
                //
                if (localisation != null) setLocation(localisation);
                else setMort();
            }
            
            // Appliquer la fin de tour
            //
            this.appelerFinDeTour();
        }
    }

    /**
     * Methode declanchee a chaque fin de tour pour chaque personnage
     */
    @Override
    public void finDeTour() {
        // Incrementer le nombre de tours passes sans manger
        //
        toursSansManger++;

        // Diminuer le nombre de points de vie si l'humain est reste plus de trois tours sans manger
        //
        if(toursSansManger > 3) pointsDeVie -= 2;
    }

    /**
     * Incemtente l'age. Peut amener a une mort de l'humain
     */
    void incrementAge() {
        age++;
        if (age > AGE_MAX) {
            setMort();
        }
    }

    /**
     * Donne naissance a de nouveaux humains.
     * Les humains naissent sur une localisation adjacente libre
     * @param humains
     *          Liste des nouveaux nes
     */
    private void donnerNaissance(List<Humain> humains) {
        // Obtenir une liste des localisations libres
        //
        List<Location> libre = field.getFreeAvailableLocations(location, 1);
        int births = naissance();
        for (int b = 0; b < births && libre.size() > 0; b++) {
            Location loc = libre.remove(0);
            Humain jeune = new Humain(nom + ":enfant" + b+1, 0, field, loc, false);
            humains.add(jeune);
        }
    }

    /**
     * @return le nombre d'enfant que l'humain peut faire
     */
    private int naissance() {
        int naissances = 0;
        if (peutEnfanter() && rand.nextDouble() <= REPRODUCTION_PROBABILITE) {
            naissances = rand.nextInt(NOMBRE_ENFANTS_MAX) + 1;
        }
        return naissances;
    }

    /**
     * @return true si l'humain est en age de faire des enfants, false sinon
     */
    boolean peutEnfanter() {
        return age >= AGE_REPRODUCTION;
    }
}
